using System.Collections.Generic;
using System.Linq;
using Behaviorize.CodeGenerator.CodeModel;
using EnvDTE;
using EnvDTE80;

namespace Behaviorize.CodeGenerator.CodeGenerating
{
	internal static class ClassCodeGenerator
	{
		private static CodeClass2 GetClass(CodeNamespace ns, Feature feature)
		{
			List<CodeClass2> classElements = ns.Children.OfType<CodeClass2>().ToList();

			switch (classElements.Count)
			{
				case 1:
					return classElements.Single();
				case 0:
					return (CodeClass2)ns.AddClass(feature.Name.AsPascal(), Access: vsCMAccess.vsCMAccessPublic);
				default:
					return (CodeClass2)(classElements.SingleOrDefault(c => c.Name == feature.Name.AsPascal())
					                    ?? classElements.SingleOrDefault(c => c.Attributes.Cast<CodeAttribute>().Any(a => a.Name == "TestClass" || a.Name == "TestFixture"))
					                    ?? ns.AddClass(feature.Name.AsPascal(), Access: vsCMAccess.vsCMAccessPublic));
			}
		}

		private static CodeNamespace GetNamespace(ProjectItem projectItem, string defaultNamespace, CodeModel.Feature feature)
		{
			List<CodeNamespace> namespaceElements = projectItem.FileCodeModel.CodeElements.OfType<CodeNamespace>().ToList();

			switch (namespaceElements.Count)
			{
				case 1:
					return namespaceElements.Single();
				case 0:
					return projectItem.FileCodeModel.AddNamespace(defaultNamespace);
				default:
					return namespaceElements.SingleOrDefault(ns => ns.Children.OfType<CodeClass>().Any(c => c.Name == feature.Name.AsPascal()))
					       ?? namespaceElements.FirstOrDefault(ns =>
					                                           ns.Children.OfType<CodeClass>().Any(
					                                           	c => c.Attributes.Cast<CodeAttribute>().Any(a => a.Name == "TestClass" || a.Name == "TestFixture")))
					       ?? namespaceElements.First();
			}
		}

		internal static string CreateOrUpdateCode(Feature feature, ProjectItem classItem, string nameSpace)
		{
			CodeNamespace ns = GetNamespace(classItem, nameSpace, feature);
			CodeClass2 testClass = GetClass(ns, feature);

			testClass.Name = feature.Name.AsPascal();

			AttributesGenerator.SetClassAndAttributes(testClass, feature);
			ScenariosCodeGenerator.CreateOrUpdateCode(feature, testClass);
			TestCodeGenerator.CreateOrUpdateCode(feature, testClass);

			testClass.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint().DeleteWhitespace(vsWhitespaceOptions.vsWhitespaceOptionsVertical);

			var textSelection = classItem.Document.Selection as TextSelection;
			
			int originalLine = textSelection.ActivePoint.Line;
			int originalCharOffset = textSelection.ActivePoint.LineCharOffset;

			textSelection.SelectAll();
			string code = textSelection.Text;

			textSelection.MoveToLineAndOffset(originalLine, originalCharOffset);

			return code;
		}
	}
}